package com.jeesuite.apigateway.filter;

import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;

import com.google.common.util.concurrent.RateLimiter;
import com.jeesuite.apigateway.helper.IpHelper;
import com.jeesuite.cache.command.RedisNumber;
import com.jeesuite.common.util.ResourceUtils;
import com.jeesuite.passport.PassportConstants;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

/**
 * 
 * @description <br>
 * @author <a href="mailto:vakinge@gmail.com">vakin</a>
 * @date 2017年7月15日
 */
public class RequestLimitFilter extends ZuulFilter implements EnvironmentAware{
	
	private static Logger log = LoggerFactory.getLogger(AccessControllerFilter.class);
	
	//private static MapCacheProvider localCacheProvider = new MapCacheProvider(1000);
	
	private static final String MSG_TOO_MANY_REQUESTS = "{\"code\": 429,\"msg\":\"Too Many Requests\"}";

	private static final String CACHEKEY_PREFIX = "reqLimit:";
	
	//令牌桶算法，每秒允许最大并发限制
	private RateLimiter limiter; 
	
	//单个人访问频次策略
	private LimitFrequencyPolicy limitFrequencyPolicy;

	@Override
	public boolean shouldFilter() {
		return Boolean.parseBoolean(ResourceUtils.getProperty("request.limit.enabled", "false"));
	}

	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 0;
	}

	@Override
	public Object run() {
		
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		
		if(limiter != null){
			//limiter.acquire();
			if(!limiter.tryAcquire(5, TimeUnit.SECONDS)){
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
				ctx.setResponseBody(MSG_TOO_MANY_REQUESTS);
				return null;
			}
		}
		
		String globalKey = request.getHeader(PassportConstants.ACCESSTOKEN);
		if(StringUtils.isBlank(globalKey)){
			globalKey = IpHelper.getIpAddr(request);
		}
		
		boolean overed = check(globalKey);
		
		if(overed){
			ctx.setSendZuulResponse(false);
			ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
			ctx.setResponseBody(MSG_TOO_MANY_REQUESTS);
			return null;
		}
		
		return null;
	}
	
	private boolean check(String key){
		key = CACHEKEY_PREFIX + key;
		
		RedisNumber redisNumber = new RedisNumber(key);
		long count = redisNumber.increase(1);
		if(count == 1){
			redisNumber.setExpire(limitFrequencyPolicy.timeSecond);
		}else if(count > limitFrequencyPolicy.limit){
			return true;
		}
		
		return false;
	}

	@Override
	public void setEnvironment(Environment environment) {
		int maxConn = Integer.parseInt(environment.getProperty("request.limit.max-connections","0"));
		if(maxConn > 0)limiter = RateLimiter.create(maxConn); 
		
		String frequency = environment.getProperty("request.limit.per-frequency","10/10s");
		String[] segments = frequency.split("/");
		
		int time = Integer.parseInt(segments[1].replaceAll("[^0-9]", ""));
		if(segments[1].toLowerCase().endsWith("m")){
			time = time * 60;
		}
		limitFrequencyPolicy = new LimitFrequencyPolicy(time, Integer.parseInt(segments[0]));
		
		log.info("per-Frequency:{}/{}s",limitFrequencyPolicy.limit,limitFrequencyPolicy.timeSecond);
	}
	
	private class LimitFrequencyPolicy{
		int timeSecond;
		int limit;
		public LimitFrequencyPolicy(int timeSecond, int limit) {
			super();
			this.timeSecond = timeSecond;
			this.limit = limit;
		}
		
		
	}

}
